﻿// Inner Fire 游戏引擎库
// GameInput - 输入管理器
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用(4)
// 用于游戏的输入管理
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-26

#include "game_input.h"
#include "klog.h"
#include <memory>

namespace ifire::game {
GameInput::GameInput(GameManager* gm) : gm_(gm) { rawInputBuffer_.fill(0); }
GameInput::~GameInput() {}
bool GameInput::Initialize(HWND hWnd) {
  hwnd_ = hWnd;

  // 两个设备，一个鼠标，一个游戏摇杆
  RAWINPUTDEVICE rid[2];

  // 配置鼠标
  rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
  rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
  // 这里有一个BUG，如果使用RIDEV_CAPTUREMOUSE会失败。这肯定要配置其它使用，比如全屏什么的
  rid[0].dwFlags = RIDEV_INPUTSINK; // | RIDEV_CAPTUREMOUSE;
  rid[0].hwndTarget = hwnd_;

  // 注册摇杆
  rid[1].usUsagePage = HID_USAGE_PAGE_GENERIC; // 通用桌面设备
  rid[1].usUsage = HID_USAGE_GENERIC_GAMEPAD;  // 游戏手柄
  rid[1].dwFlags = RIDEV_INPUTSINK;
  rid[1].hwndTarget = hwnd_;

  auto result = RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE));
  if (!result) {
    KERROR("*GameInput* RegisterRawInputDevices failed.");
  }
  return result;
}

void GameInput::ProcessInputMessage(LPARAM lParam) {
  constexpr auto HEAD_SIZE = sizeof(RAWINPUTHEADER);
  UINT size = 0;
  if (GetRawInputData(
          (HRAWINPUT)lParam, RID_INPUT, nullptr, &size, HEAD_SIZE) == -1) {
    return;
  }

  // 这里每次都需要重新分配内存，但是我使用一个已经分配好的内存，却执行错误，只能用这个内存。
  // 太奇怪了。简直不能接受。
  LPBYTE data = new BYTE[size];
  if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &size, HEAD_SIZE) !=
      size) {
    return;
  }

  RAWINPUT* raw = reinterpret_cast<RAWINPUT*>(data);
  if (raw->header.dwType == RIM_TYPEMOUSE) {
    // 处理鼠标移动
    deltaX_ += raw->data.mouse.lLastX;
    deltaY_ += raw->data.mouse.lLastY;

    // 处理按钮状态
    if (raw->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
      buttons_[0] = true;
    if (raw->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP)
      buttons_[0] = false;
    // 同样处理其他按钮...
  } else if (raw->header.dwType == RIM_TYPEHID) {
    OnJoystick(raw);
  }

  delete[] data;
}

// 手柄摇杆处理函数
void GameInput::OnJoystick(RAWINPUT* raw) {
  // 重置所有按钮状态
  eastl::fill(gamepad_buttons_.begin(), gamepad_buttons_.end(), false);

  // 获取HID设备信息
  UINT bufferSize = 0;
  GetRawInputDeviceInfo(
      raw->header.hDevice, RIDI_DEVICEINFO, NULL, &bufferSize);

  if (bufferSize == 0) return;

  RID_DEVICE_INFO deviceInfo;
  deviceInfo.cbSize = sizeof(RID_DEVICE_INFO);
  GetRawInputDeviceInfo(
      raw->header.hDevice, RIDI_DEVICEINFO, &deviceInfo, &bufferSize);

  // 只处理游戏手柄设备
  if (deviceInfo.dwType != RIM_TYPEHID ||
      deviceInfo.hid.usUsagePage != HID_USAGE_PAGE_GENERIC ||
      deviceInfo.hid.usUsage != HID_USAGE_GENERIC_GAMEPAD) {
    return;
  }

  // 获取HID预解析数据
  PHIDP_PREPARSED_DATA preparsedData = NULL;
  GetRawInputDeviceInfo(
      raw->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize);
  preparsedData = (PHIDP_PREPARSED_DATA)malloc(bufferSize);
  GetRawInputDeviceInfo(
      raw->header.hDevice, RIDI_PREPARSEDDATA, preparsedData, &bufferSize);

  // 获取设备能力
  HIDP_CAPS caps;
  auto result = HidP_GetCaps(preparsedData, &caps);
  if (result != HIDP_STATUS_SUCCESS) {
    KERROR("HidP_GetCaps failed.");
    free(preparsedData);
    return;
  }

  // 读取摇杆值
  USHORT capsNumber = caps.NumberInputValueCaps;
  PHIDP_VALUE_CAPS valueCaps =
      (PHIDP_VALUE_CAPS)malloc(sizeof(HIDP_VALUE_CAPS) * capsNumber);
  result = HidP_GetValueCaps(HidP_Input, valueCaps, &capsNumber, preparsedData);
  if (result != HIDP_STATUS_SUCCESS) {
    KERROR("HidP_GetValueCaps failed.");
    free(preparsedData);
    free(valueCaps);
    return;
  }

  // 左摇杆 - 角色移动
  left_stick_x_ = 0.0f;
  left_stick_y_ = 0.0f;
  // 右摇杆 - 视野控制
  right_stick_x_ = 0.0f;
  right_stick_y_ = 0.0f;

  USAGE usage_page, usage;

  for (USHORT i = 0; i < capsNumber; i++) {
    ULONG value;
    auto& cap = valueCaps[i];
    usage_page = cap.UsagePage;
    usage = cap.Range.UsageMin;

    // 得到值
    auto result = HidP_GetUsageValue(HidP_Input, usage_page, 0, usage, &value,
        preparsedData, (PCHAR)raw->data.hid.bRawData, raw->data.hid.dwSizeHid);
    // 如果失败，跳过
    if (result != HIDP_STATUS_SUCCESS) {
      continue;
    }

    // 处理摇杆信息
    if (usage == HID_USAGE_GENERIC_X || usage == HID_USAGE_GENERIC_Y ||
        usage == HID_USAGE_GENERIC_RX || usage == HID_USAGE_GENERIC_RY) {
      OnJoystickAxis(usage, cap, value);
    } else if (usage == HID_USAGE_GENERIC_HATSWITCH) {
      // HID_USAGE_GENERIC_HATSWITCH做为HID_USAGE_GENERIC_DPAD_UP使用
      // 我的手柄dPad失灵了。所以得到的按键不知道怎么调试。
      bool dPadUp_ = (value == 0 || value == 1 || value == 7);
      bool dPadRight_ = (value == 1 || value == 2 || value == 3);
      bool dPadDown_ = (value == 3 || value == 4 || value == 5);
      bool dPadLeft_ = (value == 5 || value == 6 || value == 7);
      if (dPadUp_) {
        gamepad_buttons_[GamepadButton::DPadUp] = true;
        gamepad_states_[GamepadButton::DPadUp].is_pushed = true;
      }
      if (dPadRight_) {
        gamepad_buttons_[GamepadButton::DPadRight] = true;
        gamepad_states_[GamepadButton::DPadRight].is_pushed = true;
      }
      if (dPadDown_) {
        gamepad_buttons_[GamepadButton::DPadDown] = true;
        gamepad_states_[GamepadButton::DPadDown].is_pushed = true;
      }
      if (dPadLeft_) {
        gamepad_buttons_[GamepadButton::DPadLeft] = true;
        gamepad_states_[GamepadButton::DPadLeft].is_pushed = true;
      }

      // KLOG("HATS 0x%x, Value: %d", usage, value);
    } else if (usage == HID_USAGE_GENERIC_Z) {
      // 本来这要处理RT和LT的。但现在出问题，无论是VALUE还是Button都检查不出来。
      // 我不钻牛角尖，这个等以后再解决。
    } else if (usage == HID_USAGE_GENERIC_RZ){
      
    }else {
      //KLOG("Unknown usage 0x%x", usage);
    }
  }

  // 释放资源
  free(valueCaps);

  // 获取按钮能力信息
  USHORT buttonCapsLength = caps.NumberInputButtonCaps;
  PHIDP_BUTTON_CAPS buttonCaps =
      (PHIDP_BUTTON_CAPS)malloc(sizeof(HIDP_BUTTON_CAPS) * buttonCapsLength);

  result = HidP_GetButtonCaps(
      HidP_Input, buttonCaps, &buttonCapsLength, preparsedData);
  if (result != HIDP_STATUS_SUCCESS) {
    KERROR("HidP_GetButtonCaps failed.");
    free(buttonCaps);
    free(preparsedData);
    return;
  }

  /////////////////////////////////////////////////////////////////////////////
  // 处理按钮
  /////////////////////////////////////////////////////////////////////////////

  // 处理按钮输入
  USAGE usageList[32]; // 假设最多32个按钮
  ULONG usageLength = 32;

  result = HidP_GetUsages(HidP_Input, buttonCaps[0].UsagePage,
      0, // LinkCollection
      usageList, &usageLength, preparsedData, (PCHAR)raw->data.hid.bRawData,
      raw->data.hid.dwSizeHid);

  // 设置被按下的按钮状态
  for (ULONG i = 0; i < usageLength; i++) {
    USAGE usage = usageList[i];
    if (usage >= 1 && usage <= gamepad_buttons_.size()) {
      gamepad_buttons_[usage - 1] = true; // 按钮通常从1开始编号
      gamepad_states_[usage - 1].is_pushed = true;
    }
  }

  // 释放资源
  free(buttonCaps);
  free(preparsedData);
}

// 添加公共方法检查按钮状态
bool GameInput::IsGamepadButtonDown(int button) const {
  return (button >= 0 && button < gamepad_buttons_.size())
             ? gamepad_buttons_[button]
             : false;
}

void GameInput::OnJoystickAxis(USAGE usage, HIDP_VALUE_CAPS& cap, ULONG value) {
  // 计算物理值(通常会得到一个以0.5为标准的值)
  auto min = cap.PhysicalMin;
  auto max = cap.PhysicalMax;
  float physical_value = (float)(value - min) / (float)((max - min) & 0xFFFF);
  // 标准化摇杆值到[-1.0, 1.0]范围
  float normalized_value = 2.0f * physical_value - 1.0f;

  // 应用死区过滤（避免摇杆微小移动）
  if (fabs(normalized_value) < dead_zone_) {
    return;
  }

  // 根据状态分配
  if (usage == HID_USAGE_GENERIC_X) {
    left_stick_x_ = normalized_value;
  } else if (usage == HID_USAGE_GENERIC_Y) {
    left_stick_y_ = normalized_value;
  } else if (usage == HID_USAGE_GENERIC_RX) {
    right_stick_x_ = normalized_value;
  } else if (usage == HID_USAGE_GENERIC_RY) {
    right_stick_y_ = normalized_value;
  }
}

void GameInput::GetMouseDelta(int& dx, int& dy) const {
  dx = deltaX_;
  dy = deltaY_;
}

void GameInput::GetLeftStick(float& x, float& y) const {
  x = left_stick_x_;
  y = left_stick_y_;
}

void GameInput::GetRightStick(float& rx, float& ry) const {
  rx = right_stick_x_;
  ry = right_stick_y_;
}

void GameInput::ResetMouseDelta() {
  deltaX_ = 0;
  deltaY_ = 0;
}

bool GameInput::IsMouseButtonDown(int button) const {
  return (button >= 0 && button < 5) ? buttons_[button] : false;
}

void GameInput::SetMouseLock(bool locked) const {
  if (locked) {
    RECT rect;
    GetClientRect(hwnd_, &rect);
    ClientToScreen(hwnd_, reinterpret_cast<POINT*>(&rect.left));
    ClientToScreen(hwnd_, reinterpret_cast<POINT*>(&rect.right));
    ClipCursor(&rect);
    ShowCursor(FALSE);
  } else {
    ClipCursor(nullptr);
    ShowCursor(TRUE);
  }
}

void GameInput::OnButtonEvents(
    const std::function<void(int, InputButtonEvent)>& event_handler) {

  for (int i = 0; i < gamepad_states_.size(); i++) {
    // 得到按钮
    auto& btn = gamepad_states_[i];

    // 检查现在的状态是否已经是被Release了（每帧检查一次）
    if (btn.is_pushed && gamepad_buttons_[i] == false) {
      btn.is_released = true;
    }

    // 处理情况，分几种局面
    // 1、在上一帧之中，没有按下，但这一帧之中按下了，就要触发ButtonDown事件
    if (btn.last_pushed == false && btn.is_pushed) {
      event_handler(i, InputButtonEvent::DOWN);
    }
    // 2、这一帧中触发了IsRelease，要触发ButtonUp事件
    if (btn.is_released) {
      event_handler(i, InputButtonEvent::UP);
    }
  }
}

void GameInput::OnFrameEnd() {
  for (auto& i : gamepad_states_) {
    i.OnFrameEnd();
  }
}

} // namespace ifire::game